﻿
using Moq;
using System;
using System.Linq;
using Xunit;

namespace EmperialApps.WeatherSpark.Internal {

    public class TestAssertHelpers {

        [Fact]
        public void Equal_AssertValue_GivenEqualValues_DoesNotThrow( ) {
            var assertion = Equal.AssertValue( default( int ), i => i );
            int expected = 1;
            int actual = expected;

            Assert.DoesNotThrow( ( ) => assertion.Assert( actual, expected ) );
        }

        [Fact]
        public void Equal_AssertValue_GivenUnequalValues_Throws( ) {
            var assertion = Equal.AssertValue( default( int ), i => i );
            int expected = 1;
            int actual = 2;

            var ex = Assert.Throws<Exception>( ( ) => assertion.Assert( actual, expected ) );
        }


        [Fact]
        public void AssertExtensions_GivenActualAndExpectedValues_GivesValuesToAssertions( ) {
            object actualObject = "actual";
            object expectedObject = "expected";
            var mocks = Enumerable.Range( 0, 2 )
                .Select( i => {
                    var mock = new Mock<IAssertion<object>>( MockBehavior.Strict );
                    mock.Setup( m => m.Assert( actualObject, expectedObject ) ).Verifiable( );
                    return mock;
                } )
                .ToList( );
            var assertions = mocks.Select( m => m.Object ).ToArray( );

            AssertExtensions.AssertAll( actualObject, expectedObject, assertions );

            foreach( var mock in mocks )
                mock.Verify( );
        }

        [Fact]
        public void AssertExtensions_AssertAll_GivenPassingAssertions_DoesNotThrow( ) {
            var assertions = Enumerable.Range( 0, 3 )
                .Select( i => CreateAssertion( fail: false ) )
                .ToArray( );

            Assert.DoesNotThrow( ( ) => AssertExtensions.AssertAll( null, null, assertions ) );
        }

        [Fact]
        public void AssertExtensions_AssertAll_GivenSingleFailingAssertion_ThrowsAssertionException( ) {
            var assertions = Enumerable.Range( 0, 3 )
                .Select( i => CreateAssertion( i == 1 ) )
                .ToArray( );

            var ex = Assert.Throws<InvalidOperationException>( ( ) => AssertExtensions.AssertAll( null, null, assertions ) );
        }

        [Fact]
        public void AssertExtensions_AssertAll_GivenMultipleFailingAssertions_ThrowsAggregateException( ) {
            var assertions = Enumerable.Range( 0, 3 )
                .Select( i => CreateAssertion( i <= 1 ) )
                .ToArray( );

            var ex = Assert.Throws<AggregateException>( ( ) => AssertExtensions.AssertAll( null, null, assertions ) );

            Assert.Equal( 2, ex.InnerExceptions.Count );
        }


        private static IAssertion<object> CreateAssertion( bool fail ) {
            var mock = new Mock<IAssertion<object>>( );
            if( fail )
                mock.Setup( m => m.Assert( It.IsAny<object>( ), It.IsAny<object>( ) ) ).Throws<InvalidOperationException>( );
            return mock.Object;
        }

    }

}
